#include <BootConsole.h>
#include <Multiboot2.h>

namespace Multiboot2
{
	bool Header::HasTag(TagType type) const
	{
		uint32_t sizeLimit = Size;
		TagHeader *curTag = (TagHeader *) ((char *) this + 8);

		while (sizeLimit && curTag->Type != 0)
		{
			if (curTag->Type == type)
				return true;

			sizeLimit -= curTag->Size;
			uint64_t newPtr = (uint64_t) curTag + curTag->Size;
			while (newPtr % 8)
				newPtr++;
			curTag = (TagHeader *) newPtr;
		}

		return false;
	}

	TagHeader &Header::GetTag(TagType type) const
	{
		uint32_t sizeLimit = Size;
		TagHeader *curTag = (TagHeader *) ((char *) this + 8);

		while (sizeLimit && curTag->Type != 0)
		{
			if (curTag->Type == type)
				return *curTag;
			
			sizeLimit -= curTag->Size;
			
			uint64_t newPtr = (uint64_t) curTag + curTag->Size;
			while (newPtr % 8)
				newPtr++;
			curTag = (TagHeader *) newPtr;
		}

		throw ArgumentOutOfRangeException();
	}
	
	size_t Header::GetMemoryMapsCount() const
	{
		TagHeader &mmaps = GetTag(MemoryMap);
		uint32_t *ptr = (uint32_t *) &mmaps;
		
		return (mmaps.Size - 8) / ptr[2];
	}
	
	const Multiboot2::MemoryMap &Header::GetMemoryMap(size_t n) const
	{
		if (n >= GetMemoryMapsCount())
			throw ArgumentOutOfRangeException();
		
		TagHeader &mmaps = GetTag(MemoryMap);
		uint32_t *ptr = (uint32_t *) &mmaps;
		uint32_t singleSize = ptr[2];
		
		uint64_t firstMap = (uint64_t) &ptr[4];
		uint64_t retMap = firstMap + singleSize * n;
		
		return (const Multiboot2::MemoryMap &) *((Multiboot2::MemoryMap *) retMap);
	}
	
	size_t Header::GetSize() const
	{
		return Size;
	}
	
	bool MemoryMap::IsOver16() const
	{
		return GetBase() >= 0x1000000;
	}
	
	bool MemoryMap::IsAvailable() const
	{
		return type == 1;
	}
	
	uintptr_t MemoryMap::GetBase() const
	{
		return base;
	}
	
	size_t MemoryMap::GetLength() const
	{
		return length;
	}
}
